package vg.modules.graph_comparison.components;

import vg.modules.graph_comparison.GraphComparisonPluginGlobals;
import vg.services.graph_view_manager.interfaces.graph_comparison.AGraphComparisonView;
import vg.services.graph_view_manager.interfaces.graph_comparison.InputGraphComparisonAlgorithmProperties;
import vg.services.graph_view_manager.interfaces.graph_comparison.OutputGraphComparisonAlgorithmProperties;
import vg.services.main_manager.VGMainServiceHelper;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * Bigraph table dialog.
 *
 * @author Timur Zolotuhin (tzolotuhin@gmail.com)
 */
public class BigraphTableDialog {
    // Mutex
    private static final Object generalMutex = new Object();

    public static void show(final AGraphComparisonView graphComparisonView) {
        if (!SwingUtilities.isEventDispatchThread()) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    synchronized (generalMutex) {
                        show(graphComparisonView);
                    }
                }
            });
        } else {
            synchronized (generalMutex) {
                // create components
                if (graphComparisonView == null)
                    return;

                InnerDialog innerDialog = new InnerDialog(VGMainServiceHelper.userInterfaceManager.getMainFrame(), true, graphComparisonView);
                innerDialog.setVisible(true);
                innerDialog.dispose();
            }
        }
    }

    private static class InnerDialog extends JDialog {
        // Swing components
        private JPanel view;

        private JLabel countCasesLabel;
        private JLabel heuristicCountCasesLabel;

        private JTable vertexMatchingTable4Swing;
        private JTable heuristicVertexMatchingTable4Swing;

        private JButton okButton = null;

        public InnerDialog(JFrame frame, boolean modal, final AGraphComparisonView graphComparisonView) {
            super(frame, modal);

            if (graphComparisonView == null)
                return;

            // get current input properties for the graph comparison
            InputGraphComparisonAlgorithmProperties input = graphComparisonView.getCurrentInputGraphComparisonAlgorithmProperties();
            OutputGraphComparisonAlgorithmProperties output = graphComparisonView.estimate(input);

            // create components
            // vertex matching table
            float[][] vertexMatchingTable = output.getVertexMatchingTable();
            if (vertexMatchingTable == null)
                vertexMatchingTable = new float[0][0];

            int vertexMatchingTableRowSize = vertexMatchingTable.length;
            int vertexMatchingTableColumnSize = 0;
            if (vertexMatchingTableRowSize > 0)
                vertexMatchingTableColumnSize = vertexMatchingTable[0].length;

            vertexMatchingTable4Swing = new JTable(vertexMatchingTableRowSize, vertexMatchingTableColumnSize);
            BiGraphTableCellRenderer.currentBarrier = input.getVertexBarrier();
            for (int i = 0; i < vertexMatchingTableRowSize; i++) {
                for (int j = 0; j < vertexMatchingTableColumnSize; j++) {
                    vertexMatchingTable4Swing.setValueAt(vertexMatchingTable[i][j], i, j);
                    vertexMatchingTable4Swing.getColumnModel().getColumn(j).setCellRenderer(new BiGraphTableCellRenderer());
                }
            }
            JScrollPane vertexMatchingTableScrollPane = new JScrollPane(vertexMatchingTable4Swing, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            vertexMatchingTable4Swing.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

            // heuristic vertex matching table
            float[][] heuristicVertexMatchingTable = output.getHeuristicVertexMatchingTable();
            if (heuristicVertexMatchingTable == null)
                heuristicVertexMatchingTable = new float[0][0];

            int heuristicVertexMatchingTableRowSize = heuristicVertexMatchingTable.length;
            int heuristicVertexMatchingTableColumnSize = 0;
            if (heuristicVertexMatchingTableRowSize > 0)
                heuristicVertexMatchingTableColumnSize = heuristicVertexMatchingTable[0].length;

            heuristicVertexMatchingTable4Swing = new JTable(heuristicVertexMatchingTableRowSize, heuristicVertexMatchingTableColumnSize);
            BiGraphTableCellRenderer.currentBarrier = input.getVertexBarrier();
            for (int i = 0; i < heuristicVertexMatchingTableRowSize; i++) {
                for (int j = 0; j < heuristicVertexMatchingTableColumnSize; j++) {
                    heuristicVertexMatchingTable4Swing.setValueAt(heuristicVertexMatchingTable[i][j], i, j);
                    heuristicVertexMatchingTable4Swing.getColumnModel().getColumn(j).setCellRenderer(new BiGraphTableCellRenderer());
                }
            }
            JScrollPane heuristicVertexMatchingTableScrollPane = new JScrollPane(heuristicVertexMatchingTable4Swing, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            heuristicVertexMatchingTable4Swing.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

            final JLabel vertexMatchingTableLabel = new JLabel("Vertex matching table");
            final JLabel heuristicVertexMatchingTableLabel = new JLabel("Heuristic vertex matching table");
            final JLabel vertexBarrierLabel = new JLabel("Vertex barrier");

            final JSlider vertexBarrierSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, (int)(input.getVertexBarrier() * 100));
            vertexBarrierSlider.setMajorTickSpacing(10);
            vertexBarrierSlider.setMinorTickSpacing(2);
            vertexBarrierSlider.setPaintTicks(true);
            vertexBarrierSlider.setPaintLabels(true);

            final JCheckBox enablingHeuristicCheckBox = new JCheckBox("Enable heuristic");
            enablingHeuristicCheckBox.setSelected(VGMainServiceHelper.config.getBooleanProperty(GraphComparisonPluginGlobals.ENABLING_HEURISTIC_CONFIG_PROPERTY));

            final JCheckBox enablingStrongComparisonCheckBox = new JCheckBox("Enable strong comparison");
            enablingStrongComparisonCheckBox.setSelected(VGMainServiceHelper.config.getBooleanProperty(GraphComparisonPluginGlobals.ENABLING_STRONG_COMPARISON_CONFIG_PROPERTY));

            countCasesLabel = new JLabel("Count cases: " + output.getUpperBoundCountCases().toString());
            heuristicCountCasesLabel = new JLabel("Heuristic count cases: " + output.getUpperBoundHeuristicCountCases().toString());

            okButton = new JButton("Cancel");
            okButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    dispose();
                }
            });

            // build view
            view = new JPanel(new GridBagLayout());

            view.add(vertexBarrierLabel, new GridBagConstraints(0,0,  1,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0));
            view.add(vertexBarrierSlider, new GridBagConstraints(1,0,  1,1,  1,0,  GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  200,0));
            view.add(enablingHeuristicCheckBox, new GridBagConstraints(0,1,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
            view.add(enablingStrongComparisonCheckBox, new GridBagConstraints(0,2,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
            view.add(countCasesLabel, new GridBagConstraints(0,3,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
            view.add(heuristicCountCasesLabel, new GridBagConstraints(0,4,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
            view.add(vertexMatchingTableLabel, new GridBagConstraints(0,5,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
            view.add(vertexMatchingTableScrollPane, new GridBagConstraints(0,6,  2,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0));
            view.add(heuristicVertexMatchingTableLabel, new GridBagConstraints(0, 7, 2, 1, 1,0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0));
            view.add(heuristicVertexMatchingTableScrollPane, new GridBagConstraints(0, 8, 2, 1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
            view.add(okButton, new GridBagConstraints(0,9, 2,1, 1,0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5,5,5,5), 0,0));

            view.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));

            getContentPane().add(view);

            pack();
            setLocationRelativeTo(frame);
            setVisible(false);
            setSize(800, 600);

            vertexBarrierSlider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    JSlider source = (JSlider)e.getSource();
                    if (!source.getValueIsAdjusting()) {
                        final int newValue = vertexBarrierSlider.getValue();
                        Thread t = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                InputGraphComparisonAlgorithmProperties input = graphComparisonView.getCurrentInputGraphComparisonAlgorithmProperties();
                                input.setVertexBarrier(newValue * 0.01f);

                                graphComparisonView.setInputGraphComparisonAlgorithmProperties(input);
                                OutputGraphComparisonAlgorithmProperties output = graphComparisonView.estimate(input);
                                updateView(input, output);
                            }
                        });
                        t.setDaemon(true);
                        t.start();
                    }
                }
            });

            enablingHeuristicCheckBox.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Thread t = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            InputGraphComparisonAlgorithmProperties input = graphComparisonView.getCurrentInputGraphComparisonAlgorithmProperties();
                            input.setEnableHeuristic(enablingHeuristicCheckBox.isSelected());
                            graphComparisonView.setInputGraphComparisonAlgorithmProperties(input);

                            OutputGraphComparisonAlgorithmProperties output = graphComparisonView.estimate(input);
                            updateView(input, output);

                            VGMainServiceHelper.config.setProperty(GraphComparisonPluginGlobals.ENABLING_HEURISTIC_CONFIG_PROPERTY, Boolean.valueOf(enablingHeuristicCheckBox.isSelected()).toString());
                        }
                    });
                    t.setDaemon(true);
                    t.start();
                }
            });

            enablingStrongComparisonCheckBox.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Thread t = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            InputGraphComparisonAlgorithmProperties input = graphComparisonView.getCurrentInputGraphComparisonAlgorithmProperties();
                            input.setEnableStrongComparison(enablingStrongComparisonCheckBox.isSelected());
                            graphComparisonView.setInputGraphComparisonAlgorithmProperties(input);

                            OutputGraphComparisonAlgorithmProperties output = graphComparisonView.estimate(input);
                            updateView(input, output);

                            VGMainServiceHelper.config.setProperty(GraphComparisonPluginGlobals.ENABLING_STRONG_COMPARISON_CONFIG_PROPERTY, Boolean.valueOf(enablingStrongComparisonCheckBox.isSelected()).toString());
                        }
                    });
                    t.setDaemon(true);
                    t.start();
                }
            });
        }

        private void updateView(final InputGraphComparisonAlgorithmProperties input, final OutputGraphComparisonAlgorithmProperties output) {
            if (!SwingUtilities.isEventDispatchThread()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        updateView(input, output);
                    }
                });
            } else {
                countCasesLabel.setText("Count cases: " + output.getUpperBoundCountCases().toString());
                heuristicCountCasesLabel.setText("Heuristic count cases: " + output.getUpperBoundHeuristicCountCases().toString());

                BiGraphTableCellRenderer.currentBarrier = output.getVertexBarrier();

                // vertex matching table
                float[][] vertexMatchingTable = output.getVertexMatchingTable();
                if (vertexMatchingTable == null)
                    vertexMatchingTable = new float[0][0];

                int vertexMatchingTableRowSize = vertexMatchingTable.length;
                int vertexMatchingTableColumnSize = 0;
                if (vertexMatchingTableRowSize > 0)
                    vertexMatchingTableColumnSize = vertexMatchingTable[0].length;

                BiGraphTableCellRenderer.currentBarrier = input.getVertexBarrier();
                for (int i = 0; i < vertexMatchingTableRowSize; i++) {
                    for (int j = 0; j < vertexMatchingTableColumnSize; j++) {
                        vertexMatchingTable4Swing.setValueAt(vertexMatchingTable[i][j], i, j);
                    }
                }

                // heuristic vertex matching table
                float[][] heuristicVertexMatchingTable = output.getHeuristicVertexMatchingTable();
                if (heuristicVertexMatchingTable == null)
                    heuristicVertexMatchingTable = new float[0][0];

                int heuristicVertexMatchingTableRowSize = heuristicVertexMatchingTable.length;
                int heuristicVertexMatchingTableColumnSize = 0;
                if (heuristicVertexMatchingTableRowSize > 0)
                    heuristicVertexMatchingTableColumnSize = heuristicVertexMatchingTable[0].length;

                BiGraphTableCellRenderer.currentBarrier = input.getVertexBarrier();
                for (int i = 0; i < heuristicVertexMatchingTableRowSize; i++) {
                    for (int j = 0; j < heuristicVertexMatchingTableColumnSize; j++) {
                        heuristicVertexMatchingTable4Swing.setValueAt(heuristicVertexMatchingTable[i][j], i, j);
                    }
                }

                vertexMatchingTable4Swing.updateUI();
                heuristicVertexMatchingTable4Swing.updateUI();
            }
        }
    }

    private static class BiGraphTableCellRenderer extends DefaultTableCellRenderer {
        public static float currentBarrier = 1.0f;

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {

            //Cells are by default rendered as a JLabel.
            JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
            l.setText(String.format("%.2f", (Float)value * 100));

            if ((Float)value >= currentBarrier) {
                l.setBackground(Color.GREEN);
            } else {
                l.setBackground(Color.WHITE);
            }

            //Return the JLabel which renders the cell.
            return l;
        }
    }
}
